package LunaServer.routing;

import com.sun.net.httpserver.HttpExchange;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.tools.StandardJavaFileManager;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

import LunaServer.config.ConfigManager;
import LunaServer.core.LunaServer;

/**
 * 路由处理器类，支持从文件加载路由处理逻辑
 * 每一个路由都必须实现Route接口
 */
public class RouteHandler {

    // 缓存已编译的类，避免重复编译
    private static final ConcurrentHashMap<String, Class<?>> classCache = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Long> fileModifiedTime = new ConcurrentHashMap<>();

    // 配置管理器实例
    private static ConfigManager configManager = new ConfigManager();

    /**
     * 从指定Java文件加载并执行路由处理逻辑
     * 
     * @param filePath 包含路由处理逻辑的Java文件路径
     * @param exchange HttpExchange对象
     * @return 处理结果
     */
    public static String handleRouteFromFile(LunaServer serverInstance, String filePath, HttpExchange exchange) {
        return handleRouteFromFile(serverInstance, filePath, exchange, false);
    }

    /**
     * 从指定Java文件加载并执行路由处理逻辑
     * 
     * @param filePath       包含路由处理逻辑的Java文件路径
     * @param exchange       HttpExchange对象
     * @param forceRecompile 是否强制重新编译
     * @return 处理结果
     */
    public static String handleRouteFromFile(LunaServer serverInstance, String filePath, HttpExchange exchange,
            boolean forceRecompile) {
        try {
            // 检查文件是否存在
            if (!Files.exists(Paths.get(filePath))) {
                sendErrorResponse(exchange, 404, "Error: Route handler file not found: " + filePath);
                return null;
            }

            // 获取文件最后修改时间
            long lastModified = new File(filePath).lastModified();
            Long cachedModified = fileModifiedTime.get(filePath);

            // 检查是否需要重新编译（热重载）
            boolean needsRecompile = forceRecompile || (cachedModified == null || lastModified > cachedModified);

            // 获取类名（基于文件名）
            String className = getClassNameFromFileName(filePath);

            Class<?> handlerClass = null;

            // 如果需要更新，或者类列表里没有东西
            if (needsRecompile || !classCache.containsKey(filePath)) {
                // 编译Java文件
                handlerClass = compileJavaFile(filePath, className);
                if (handlerClass != null) {
                    classCache.put(filePath, handlerClass);
                    fileModifiedTime.put(filePath, lastModified);
                }
            } else {
                // 使用缓存的类
                handlerClass = classCache.get(filePath);
            }

            // 失败
            if (handlerClass == null) {
                sendErrorResponse(exchange, 500, "Error: Failed to compile or load class from file: " + filePath);
                return null;
            }

            // 实例化类并调用处理方法
            return executeHandlerClass(serverInstance, handlerClass, exchange);
        } catch (Exception e) {
            e.printStackTrace();
            sendErrorResponse(exchange, 500, "Error processing route from file: " + e.getMessage());
            return null;
        }
    }

    /**
     * 发送错误响应
     * 
     * @param exchange   HttpExchange对象
     * @param statusCode HTTP状态码
     * @param message    错误消息
     */
    private static void sendErrorResponse(HttpExchange exchange, int statusCode, String message) {
        try {
            exchange.sendResponseHeaders(statusCode, message.getBytes().length);
            OutputStream os = exchange.getResponseBody();
            os.write(message.getBytes());
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从文件名获取类名
     * 
     * @param filePath 文件路径
     * @return 类名
     */
    private static String getClassNameFromFileName(String filePath) {
        File file = new File(filePath);
        String fileName = file.getName();
        if (fileName.contains(".")) {
            return fileName.substring(0, fileName.lastIndexOf('.'));
        }
        return fileName;
    }

    /**
     * 编译Java文件
     * 
     * @param filePath  Java文件路径
     * @param className 类名
     * @return 编译后的Class对象
     */
    private static Class<?> compileJavaFile(String filePath, String className) {
        try {
            // 获取系统Java编译器
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            if (compiler == null) {
                throw new RuntimeException("无法获取系统Java编译器，请确保使用JDK而不是JRE运行程序");
            }

            // 创建诊断收集器
            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

            // 获取标准文件管理器
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

            // 创建编译任务
            Iterable<? extends JavaFileObject> compilationUnits = fileManager
                    .getJavaFileObjectsFromStrings(Arrays.asList(filePath));

            // 编译选项 - 从配置中读取classpath
            String classpath = getClasspathFromConfig();
            Iterable<String> options = Arrays.asList("-cp", classpath, "-encoding", "UTF-8");

            // 执行编译
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
                    compilationUnits);
            Boolean result = task.call();

            fileManager.close();

            if (result == Boolean.TRUE) {
                // 编译成功，加载类
                return loadClass(className, filePath);
            } else {
                // 编译失败，输出错误信息
                StringBuilder errorMsg = new StringBuilder("编译错误:\n");
                for (var diagnostic : diagnostics.getDiagnostics()) {
                    errorMsg.append(diagnostic.toString()).append("\n");
                }
                throw new RuntimeException(errorMsg.toString());
            }
        } catch (Exception e) {
            throw new RuntimeException("编译Java文件时出错: " + e.getMessage(), e);
        }
    }

    /**
     * 从配置中获取路由编译的classpath
     * 
     * @return classpath字符串
     */
    private static String getClasspathFromConfig() {
        java.util.List<String> classpathList = configManager.getRouteDependencyList("classpath",
                java.util.Arrays.asList("src", "lib/gson-gson-parent-2.13.2/gson/target/classes",
                        "lib/gson-gson-parent-2.13.2/gson/lib/*", "."));
        return String.join(";", classpathList);
    }

    /**
     * 加载编译后的类
     * 
     * @param className    类名
     * @param javaFilePath Java文件路径
     * @return 加载的Class对象
     */
    private static Class<?> loadClass(String className, String javaFilePath) {
        try {
            // 获取类文件路径
            File javaFile = new File(javaFilePath);
            File classFile = new File(javaFile.getParentFile(), className + ".class");

            // 创建URL类加载器
            URL[] urls = { javaFile.getParentFile().toURI().toURL() };
            URLClassLoader classLoader = new URLClassLoader(urls);
            try {
                // 加载类
                return classLoader.loadClass(className);
            } finally {
                // 关闭 classloader 以释放文件句柄等资源（避免类/文件句柄泄漏）
                try {
                    classLoader.close();
                } catch (IOException ioe) {
                    // 记录但不影响路由加载逻辑
                    ioe.printStackTrace();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("加载类时出错: " + e.getMessage(), e);
        }
    }

    /**
     * 执行处理类
     * 
     * @param handlerClass 处理类
     * @param exchange     HttpExchange对象
     * @return 处理结果
     */
    private static String executeHandlerClass(LunaServer serverInstance, Class<?> handlerClass, HttpExchange exchange) {
        try {
            // 检查类是否实现了Route接口
            if (Route.class.isAssignableFrom(handlerClass)) {
                // 实例化，传入服务器上下文的（const）引用实例
                Route routeInstance = (Route) handlerClass.getDeclaredConstructor(LunaServer.class)
                        .newInstance(serverInstance);
                return routeInstance.handleRequest(exchange);
            } else {
                throw new RuntimeException("路由处理类必须实现Route接口: " + handlerClass.getName());
            }
        } catch (Exception e) {
            throw new RuntimeException("执行处理类时出错: " + e.getMessage(), e);
        }
    }

    /**
     * 清除类缓存（用于强制重新加载）
     * 
     * @param filePath 文件路径
     */
    public static void clearCache(String filePath) {
        classCache.remove(filePath);
        fileModifiedTime.remove(filePath);
    }

    /**
     * 清除所有缓存
     */
    public static void clearAllCache() {
        classCache.clear();
        fileModifiedTime.clear();
    }
}